﻿namespace Assembler_Compiler.IntelliSense
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;

    public class CodeHighlight
    {
        private List<string> lines;
        private NumberedTextBoxUC textBox;
        private List<string> reserved = new List<string>(new[] { "ORG", "PROC", "ENDP", "ENDS", "END", "RET" });
        private List<string> functions = new List<string>(new[] { "MOV", "LEA", "INT", "JMP", "XCHG", "RET", "DB", "DW", "DD" });

        private List<string> registers = new List<string>(new[]
                                                              {
                                                                  "AL", "BL", "CL", "DL", "AH", "BH", "CH", "DH", "AX",
                                                                  "BX", "CX", "DX", "SI", "DI", "BP", "SP"
                                                              });


        public CodeHighlight(NumberedTextBoxUC textBox)
        {
            this.textBox = textBox;
            this.textBox.TextChanged += this.Highlight;
            this.lines = new List<string>(textBox.Lines);
        }

        public void HighLightAll()
        {
            this.lines = new List<string>();
            this.Highlight(null, null);
        }

        private void Highlight(object sender, EventArgs e)
        {
            List<int> changedIndexes = this.GetChangedLinesIndex();

            int cursorLocation = this.textBox.SelectionStart;
            foreach (var index in changedIndexes)
            {
                int start = this.textBox.GetFirstCharIndexFromLine(index);
                this.textBox.Select(start, this.textBox.Lines[index].Length);
                this.textBox.SelectionColor = Color.Black;
                this.textBox.SelectionFont = new Font(this.textBox.SelectionFont, FontStyle.Regular);
                this.HighlightCommentsAndVars(this.textBox.Lines[index], index);
                this.HighlightWords(this.textBox.Lines[index], index);
            }

            this.textBox.Select(cursorLocation, 0);

            this.lines = new List<string>(this.textBox.Lines);
        }

        private void HighlightCommentsAndVars(string line, int index)
        {
            string[] sublines = line.Split(new[] { ';' }, 2);
            if (sublines.Length == 2)
            {
                string s = ";" + sublines[1];
                int i = 0;
                int offset = this.textBox.GetFirstCharIndexFromLine(index);
                int start = this.textBox.Lines[index].IndexOf(s) + offset;
                this.textBox.Select(start, s.Length);
                this.textBox.SelectionColor = Color.LimeGreen;
            }

            this.HighlightVars(sublines[0], index);
        }

        private void HighlightWords(string line, int index)
        {
            string[] words = line.Split(new[] { ';' }, 2)[0].Split(' ');
            foreach (var word in words)
            {
                if (this.functions.Contains(word))
                {
                    int start = this.textBox.Lines[index].IndexOf(word) +
                                this.textBox.GetFirstCharIndexFromLine(index);
                    this.textBox.Select(start, word.Length);
                    this.textBox.SelectionColor = Color.Blue;
                    this.textBox.SelectionFont = new Font(this.textBox.SelectionFont, FontStyle.Bold);
                }

                if (this.reserved.Contains(word))
                {
                    int start = this.textBox.Lines[index].IndexOf(word) +
                                this.textBox.GetFirstCharIndexFromLine(index);
                    this.textBox.Select(start, word.Length);
                    this.textBox.SelectionColor = Color.DarkGray;
                    this.textBox.SelectionFont = new Font(this.textBox.SelectionFont, FontStyle.Bold);
                }

                string[] subwords = word.Split(',');
                foreach (var subword in subwords)
                {
                    string w = subword.TrimEnd(new[] { ' ' }).TrimStart(new[] { ' ' });
                    if (this.registers.Contains(w))
                    {
                        int start = this.textBox.Lines[index].IndexOf(w) +
                                    this.textBox.GetFirstCharIndexFromLine(index);
                        this.textBox.Select(start, w.Length);
                        this.textBox.SelectionColor = Color.LightCoral;
                    }       
                }
            }
        }

        private void HighlightVars(string line, int index)
        {
            string[] words = line.Split('\'');
            for (int i = 1; i < words.Length; i += 2)
            {
                int start = this.textBox.Lines[index].IndexOf(words[i]) +
                                this.textBox.GetFirstCharIndexFromLine(index);
                this.textBox.Select(start, words[i].Length);
                this.textBox.SelectionColor = Color.BlueViolet;
            }
        }

        private List<int> GetChangedLinesIndex()
        {
            List<int> res = new List<int>();
            if (this.textBox.Lines.Length == this.lines.Count)
            {
                for (int i = 0; i < this.textBox.Lines.Length && i < this.lines.Count; i++)
                {
                    if (this.lines[i] != this.textBox.Lines[i])
                    {
                        res.Add(i);
                    }
                }

                return res;
            }

            if (this.textBox.Lines.Length > this.lines.Count)
            {
                for (int i = this.lines.Count; i < this.textBox.Lines.Length; i++)
                {
                    res.Add(i);
                    this.lines.Add(this.textBox.Lines[i]);
                }

                res.AddRange(this.GetChangedLinesIndex());
                return res;
            }

            if (this.textBox.Lines.Length < this.lines.Count)
            {
                for (int i = 0; i < this.textBox.Lines.Length; i++)
                {
                    if (this.lines[i] != this.textBox.Lines[i])
                    {
                        res.Add(i);
                    }
                }

                return res;
            }

            return res;
        }
    }
}
